﻿
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

namespace CatEars.Text.Match
{
    /// <summary>
    /// 特殊检索类
    /// </summary>
    public class SpecialMatch
    {
        /// <summary>
        /// 获取帮助文本
        /// </summary>
        /// <returns>帮助文本</returns>
        public static string GetHelpText()
        {
            StringBuilder strDisplay = new StringBuilder();
            strDisplay.Append("符号说明：\r\n");
            strDisplay.Append("    1、通配符：%\r\n");
            strDisplay.Append("    2、与匹配：&&\r\n");
            strDisplay.Append("    3、或匹配：||\r\n");
            strDisplay.Append("    4、非运算符：^(Shitf+6)，在原有匹配串前面加^表示不匹配，加在其它位置当作普通字符\r\n");
            strDisplay.Append("    5、&&优先级比||高，即先匹配与关系再匹配或关系\r\n");
            strDisplay.Append("    6、除非需要匹配字符串中的空格，否则不要随意添加\" \"");
            strDisplay.Append("\r\n");
            strDisplay.Append("例子：\r\n");
            strDisplay.Append("    1、含有A和B：%A%&&%B%\r\n");
            strDisplay.Append("    2、含有A或B：%A%||%B%\r\n");
            strDisplay.Append("    3、含有A和B且A在B之前：%A%B%\r\n");
            strDisplay.Append("    4、以A开头：A%\r\n");
            strDisplay.Append("    5、以B结尾：%B\r\n");
            strDisplay.Append("    6、以A开头以B结尾：A%B\r\n");
            strDisplay.Append("    7、不含有A：^%A%\r\n");
            strDisplay.Append("    8、不含有A和B：^%A%&&^%B%\r\n");
            strDisplay.Append("    9、不含有A或B：^%A%||^%B%\r\n");
            strDisplay.Append("    10、含有A且不含B：%A%&&^%B%\r\n");
            return strDisplay.ToString();
        }

        /// <summary>
        /// 单例对象
        /// </summary>
        public static SpecialMatch Instance = new SpecialMatch();

        #region 特殊检索功能

        /// <summary>
        /// 编译正则表达式提高匹配速度
        /// </summary>
        public bool CompiledRegex { get; set; }

        /// <summary>
        /// 结果缓存（只缓存最后一次搜索）
        /// </summary>
        string m_strlastFind = null;

        /// <summary>
        /// 结果缓存（只缓存最后一次搜索）
        /// </summary>
        List<List<string>> m_lastFormat = null;

        /// <summary>
        /// 结果缓存（只缓存最后一次搜索）
        /// </summary>
        Dictionary<string, Regex> m_dictRegex = new Dictionary<string, Regex>();

        /// <summary>
        /// 判断Value与设置的Find是否匹配
        /// </summary>
        /// <param name="strValue"></param>
        /// <param name="strFind"></param>
        /// <returns></returns>
        public bool IsMatch(string strValue, string strFind)
        {
            if (!IsSpecial(strFind))
            {
                return strValue.Contains(strFind);
            }
            else
            {
                // %A%&&%B%||%C%&&%D%  拆分后用正则表达式匹配
                List<List<string>> format = GetSpecialFormat(strFind);
                foreach (var or in format)
                {
                    bool blnMatch = true;
                    foreach (var and in or)
                    {
                        string strAnd0;
                        bool blnNot = and.StartsWith("^") && and.Length > 1;
                        if (blnNot)
                        {
                            strAnd0 = and.Substring(1);
                        }
                        else
                        {
                            strAnd0 = and;
                        }
                        Regex regex;
                        if (m_dictRegex.TryGetValue(strAnd0, out regex) == false)
                        {
                            regex = GetRegex(strAnd0);
                            m_dictRegex[strAnd0] = regex;
                        }
                        if (regex.IsMatch(strValue) == blnNot)
                        {
                            blnMatch = false;
                            break;
                        }
                    }
                    if (blnMatch)
                    {
                        return true;
                    }
                }
                return false;
            }
        }

        /// <summary>
        /// 搜索串生成正则表达式
        /// </summary>
        /// <param name="strAnd0"></param>
        /// <returns></returns>
        public Regex GetRegex(string strAnd0)
        {
            var strAnd = RegexEx.DoEscape(strAnd0);
            strAnd = strAnd.Replace("%", ".*");
            Regex regex;
            if (CompiledRegex) regex = new Regex("^" + strAnd + "$", RegexOptions.Compiled);
            else regex = new Regex("^" + strAnd + "$", RegexOptions.Compiled);
            return regex;
        }

        /// <summary>
        /// 判断字符串是否启用特殊检索
        /// </summary>
        /// <param name="strFind"></param>
        /// <returns></returns>
        public static bool IsSpecial(string strFind)
        {
            return (strFind.Contains("%")
                || strFind.Contains("&&")
                || strFind.Contains("||")
                || strFind.Contains("^"));
        }

        /// <summary>
        /// 格式化特殊检索,结合IsSpecial使用(可用于生成sql)
        /// </summary>
        /// <param name="strFind"></param>
        /// <returns>第一级list是or关系 第二级list是and关系</returns>
        public List<List<string>> GetSpecialFormat(string strFind)
        {
            if (!IsSpecial(strFind))
            {
                List<List<string>> result = new List<List<string>>();
                List<string> tmp = new List<string>();
                tmp.Add("%" + strFind + "%");
                result.Add(tmp);
                return result;
            }
            else
            {
                List<List<string>> result = null;
                if (m_strlastFind == strFind)
                {
                    result = m_lastFormat;
                }
                if (result == null)
                {
                    var or = strFind.Split(new string[] { "||" }, StringSplitOptions.RemoveEmptyEntries);
                    result = new List<List<string>>(or.Length);
                    for (int i = 0; i < or.Length; i++)
                    {
                        string strKey = or[i];
                        var and = strKey.Split(new string[] { "&&" }, StringSplitOptions.RemoveEmptyEntries);
                        List<string> tmp = new List<string>(and);
                        result.Add(tmp);
                    }

                    m_lastFormat = result;
                    m_strlastFind = strFind;
                    m_dictRegex.Clear();
                }
                return result;
            }
        }
        #endregion
    }
}
